package com.sbm.quartz;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.TimeZone;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ResourceUtils;
import org.yaml.snakeyaml.TypeDescription;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.nodes.Tag;

import com.alibaba.fastjson.JSON;
import com.thoughtworks.xstream.XStream;

/**
 * 任务加载器 接受多种任务来源 文件来源（json/xml/yaml/properties） 数据库来源（暂未完成）
 *
 * @author Administrator
 */
@Component
public class TaskLoader {
    private static final Logger LOGGER = LoggerFactory.getLogger(TaskLoader.class);

    private static final String DateFormat = "yyyy-MM-dd HH:mm:ss";

    @Autowired
    private QuartzConfig config;

    public List<TaskInfo> getTasks() {
        TimeZone tz = TimeZone.getTimeZone(this.config.getTimeZone());
        TimeZone.setDefault(tz);
        List<TaskInfo> tasks = new ArrayList<TaskInfo>();
        if (this.config.getDataSource() == null || this.config.getDataSource() == "")
            return tasks;
        try {
            String type = this.config.getDataSource().toLowerCase();
            if (type.endsWith(".json") || type.endsWith(".xml") || type.endsWith(".properties")
                    || type.endsWith(".yml")) {
                type = type.substring(type.lastIndexOf(".") + 1, type.length());
                File file = ResourceUtils.getFile("classpath:" + this.config.getDataSource());
                if (!file.exists())
                    throw new Exception("任务列表文件不存在");
                switch (type) {
                case "json":
                    tasks = fromJson(file);
                    break;
                case "xml":
                    tasks = fromXml(file);
                    break;
                case "properties":
                    tasks = fromProperties(file);
                    break;
                case "yml":
                    tasks = fromYaml(file);
                    break;
                default:
                    throw new Exception("不支持文件格式");
                }
            } else if (type.equals("database")) {

            } else
                throw new Exception("未知来源");
        } catch (Exception ex) {
            LOGGER.error("读取" + this.config.getDataSource() + "时发生异常：" + ex.getMessage());
        }
        return tasks;
    }

    private List<TaskInfo> fromJson(File file) throws Exception {
        String content = readFromFile(file);
        return JSON.parseArray(content, TaskInfo.class);
    }

    @SuppressWarnings("unchecked")
    private List<TaskInfo> fromXml(File file) throws FileNotFoundException {
        XStream reader = new XStream();
        FileInputStream stream = new FileInputStream(file);
        BufferedInputStream read = new BufferedInputStream(stream);
        reader.alias("Tasks", List.class);
        reader.alias("Task", TaskInfo.class);
        XStream.setupDefaultSecurity(reader);
        reader.allowTypeHierarchy(TaskInfo.class);
        reader.registerConverter(new XStreamDateConverter(DateFormat));
        return (List<TaskInfo>) reader.fromXML(read);
    }

    @SuppressWarnings("unchecked")
    private List<TaskInfo> fromYaml(File file) throws Exception {
        Constructor constructor = new Constructor();
        constructor.addTypeDescription(new TypeDescription(TaskInfo.class, new Tag("!Task")));
        Yaml yaml = new Yaml(constructor);
        FileInputStream read = null;
        try {
            read = new FileInputStream(file);
            return yaml.loadAs(read, new ArrayList<TaskInfo>().getClass());
        } catch (Exception ex) {
            throw ex;
        } finally {
            try {
                if (read != null)
                    read.close();
            } catch (Exception ex) {
                LOGGER.error("关闭Stream时发生异常：" + ex.getMessage());
            }
        }
    }

    private List<TaskInfo> fromProperties(File file) throws Exception {
        Properties properties = new Properties();
        FileInputStream read = null;
        try {
            List<TaskInfo> tasks = new ArrayList<TaskInfo>();
            read = new FileInputStream(file);
            properties.load(read);
            for (String tk : properties.getProperty("Tasks").split(",")) {
                TaskInfo task = new TaskInfo();
                for (Field field : task.getClass().getDeclaredFields()) {
                    String prefix = "Task." + tk.trim() + "." + field.getName();
                    if (field.getType().isAssignableFrom(Map.class)) {
                        Map<String, Object> map = new HashMap<String, Object>();
                        for (Map.Entry<Object, Object> entry : properties.entrySet()) {
                            if (entry.getKey().toString().trim().startsWith(prefix)) {
                                String mapKey = entry.getKey().toString().trim().replaceAll(prefix + ".", "");
                                String mapValue = entry.getValue().toString().trim();
                                map.put(mapKey, mapValue);
                            }
                        }
                        Method setter = task.getClass().getDeclaredMethod(this.getSetterName(field.getName()),
                                Map.class);
                        setter.invoke(task, map);
                    } else {
                        for (Map.Entry<Object, Object> entry : properties.entrySet()) {
                            if (entry.getKey().toString().trim().equals(prefix)) {
                                Object value = null;
                                if (field.getType().isAssignableFrom(String.class))
                                    value = entry.getValue().toString().trim();
                                else if (field.getType().isAssignableFrom(Date.class))
                                    value = new SimpleDateFormat(DateFormat).parse(entry.getValue().toString().trim());
                                else if(field.getType().isAssignableFrom(boolean.class))
                                    value = Boolean.parseBoolean(entry.getValue().toString().trim());
                                Method setter = task.getClass().getDeclaredMethod(this.getSetterName(field.getName()),
                                        field.getType());
                                setter.invoke(task, value);
                                break;
                            }
                        }
                    }
                }
                tasks.add(task);
            }
            return tasks;
        } catch (Exception ex) {
            throw ex;
        } finally {
            try {
                if (read != null)
                    read.close();
            } catch (Exception ex) {
                LOGGER.error("关闭Stream时发生异常：" + ex.getMessage());
            }
        }
    }

    private String readFromFile(File file) throws Exception {
        FileInputStream read = null;
        InputStreamReader input = null;
        BufferedReader reader = null;
        try {
            read = new FileInputStream(file);
            input = new InputStreamReader(read);
            reader = new BufferedReader(input);
            String content = "";
            String line = "";
            while ((line = reader.readLine()) != null)
                content += line;
            return content;
        } catch (Exception ex) {
            throw ex;
        } finally {
            try {
                if (reader != null)
                    reader.close();
                if (input != null)
                    input.close();
                if (read != null)
                    read.close();
            } catch (Exception ex) {
                LOGGER.error("关闭Stream时发生异常：" + ex.getMessage());
            }
        }
    }

    private String getSetterName(String fieldName) {
        return "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1, fieldName.length());
    }
}
